package cn.itcast.hiss.process.activiti.handler.processinstance;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.itcast.hiss.api.client.HissProcessConstants;
import cn.itcast.hiss.api.client.processdefinition.ProcessViewerModel;
import cn.itcast.hiss.api.client.processdefinition.viewer.*;
import cn.itcast.hiss.cmd.handler.CmdHandler;
import cn.itcast.hiss.common.enums.ActiveNodeTypeEnum;
import cn.itcast.hiss.common.enums.ModelTypeEnum;
import cn.itcast.hiss.common.enums.ProcessNodeStatusEnum;
import cn.itcast.hiss.common.enums.ProcessStatusEnum;
import cn.itcast.hiss.handler.HandlerIdClientEnum;
import cn.itcast.hiss.message.Message;
import cn.itcast.hiss.message.MessageContext;
import cn.itcast.hiss.message.sender.processdefinition.ProcessViewerModelMessage;
import cn.itcast.hiss.process.activiti.mapper.HissProcessFormMapper;
import cn.itcast.hiss.process.activiti.mapper.HissProcessPreLaunchMapper;
import cn.itcast.hiss.process.activiti.mapper.HissSystemUserMapper;
import cn.itcast.hiss.process.activiti.pojo.HissProcessForm;
import cn.itcast.hiss.process.activiti.pojo.HissProcessPreLaunch;
import cn.itcast.hiss.process.activiti.pojo.HissSystemUser;
import cn.itcast.hiss.process.activiti.service.CommentService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.repository.Model;
import org.activiti.engine.task.IdentityLink;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Component
public class PI_GetInstanceViewerHandler implements CmdHandler<ProcessViewerModelMessage> {

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    HistoryService historyService;

    @Autowired
    TaskService taskService;

    @Autowired
    CommentService commentService;

    @Autowired
    HissProcessFormMapper hissProcessFormMapper;

    @Autowired
    HissSystemUserMapper hissSystemUserMapper;

    @Autowired
    HissProcessPreLaunchMapper hissProcessPreLaunchMapper;

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void invoke(Message params, MessageContext messageContext) {
        ProcessViewerModel processViewerModel = (ProcessViewerModel)params.getPalyload();
        String processInstanceId = processViewerModel.getProcessInstanceId();
        HissProcessPreLaunch hissProcessPreLaunch = hissProcessPreLaunchMapper.selectById(processInstanceId);
        if(hissProcessPreLaunch!=null){
            loadLaunch(processViewerModel,hissProcessPreLaunch);
        }else{
            HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(processInstanceId, processViewerModel);
            //查询流程变量——在解析节点类型时需要
            List<HistoricVariableInstance> variableInstances = historyService
                    .createHistoricVariableInstanceQuery().processInstanceId(historicProcessInstance.getId()).list();
            Map<String,Object> variables = new HashMap<>();
            for (HistoricVariableInstance variableInstance : variableInstances) {
                variables.put(variableInstance.getVariableName(),variableInstance.getValue());
            }
            // 写基本信息
            processInfo(historicProcessInstance,processViewerModel,variables);
            // 解析是否为管理员
            HissSystemUser hissSystemUser = hissSystemUserMapper.selectById(params.getMessageAuth().getCurrentUser().getUserId());
            if (hissSystemUser != null) {
                processViewerModel.getProcessInfo().setAdmin(true);
            }
            // 查询高亮活动的信息
            parseHighLightedData(historicProcessInstance,processViewerModel,variables);
            // 加载绑定的表单数据
            setProcessForm(historicProcessInstance,processViewerModel,null);
        }

        // 写回数据
        messageContext.addResultAndCount("viewer",processViewerModel);
    }



    /**
     * 设置流程表单信息
     * @param processInstance
     * @param processViewerModel
     */
    private void setProcessForm(HistoricProcessInstance processInstance,ProcessViewerModel processViewerModel,String launchId){
        LambdaQueryWrapper<HissProcessForm> wrapper = Wrappers.lambdaQuery();
        // 管理员，在流程中心查询，因此不用过滤
        if(processInstance!=null&&!processViewerModel.getProcessInfo().getAdmin()){
            wrapper.eq(HissProcessForm::getTenantId,processInstance.getTenantId());
        }
        if(StrUtil.isNotEmpty(launchId)){
            wrapper.eq(HissProcessForm::getLaunchId,launchId);
        }
        if( processInstance!=null ){
            wrapper.eq(HissProcessForm::getProcessInstanceId, processInstance.getId());
        }
        List<HissProcessForm> hissProcessForms = hissProcessFormMapper.selectList(wrapper);
        processViewerModel.setForms(hissProcessForms.stream().map(item-> BeanUtil.copyProperties(item,ProcessFormInfo.class)).collect(Collectors.toList()));
        // 设置流程配置内容
        if(processInstance!=null) {
            List<String> deploymentResourceNames = repositoryService.getDeploymentResourceNames(processInstance.getDeploymentId());
            if (deploymentResourceNames.contains(HissProcessConstants.PROCESS_SHOW_CONFIG_JSON)) {
                InputStream inputStream = repositoryService.getResourceAsStream(processInstance.getDeploymentId(), HissProcessConstants.PROCESS_SHOW_CONFIG_JSON);
                if (inputStream != null) {
                    processViewerModel.setConfigJson(IoUtil.readUtf8(inputStream));
                }
            }
        }
    }

    /**
     * 查询高亮活动信息
     *
     * @param historicProcessInstance
     * @param processViewerModel
     * @param variables
     */
    private void parseHighLightedData(HistoricProcessInstance historicProcessInstance, ProcessViewerModel processViewerModel, Map<String, Object> variables) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
        Process mainProcess = bpmnModel.getMainProcess();

        //查询出当前实例所有需要高亮的活动节点
        List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(historicProcessInstance.getId()).orderByHistoricActivityInstanceStartTime().desc().list();
        Map<String, List<ViewerNodeInfo>> highlights = processViewerModel.getHighlights();

        // 记录已经启动了的活动ID
        Set<String> startedActivityIds = new HashSet<>();
        // 记录当前正激活的活动id
        Set<String> activeActivityIds = new HashSet<>();
        //查询评论信息
        List<ActHiComment> actHiComments = commentService.selectByProcessInstanceId(historicProcessInstance.getId());
        processViewerModel.setComments(actHiComments);
        //Map<String, ActHiComment> commentMap = actHiComments.stream().collect(Collectors.toMap(ActHiComment::getTaskId, Function.identity()));

        Map<String , ActHiComment> commentMap = new HashMap<>();
        for (ActHiComment comment : actHiComments){
            if(!commentMap.containsKey(comment.getTaskId())){
                commentMap.put(comment.getTaskId(),comment);
            }
        }
        //遍历赋值
        for (HistoricActivityInstance activityInstance : activityInstances) {
            String activityId = activityInstance.getActivityId();
            List<ViewerNodeInfo> list = highlights.getOrDefault(activityId,new ArrayList<>());
            ViewerNodeInfo viewerNodeInfo = new ViewerNodeInfo();
            boolean isActive = false;
            if(activityInstance.getEndTime()!=null){
                viewerNodeInfo.setStatus(ProcessNodeStatusEnum.COMPLETE);
                //把取消状态的节点也添加到集合中
                if (StrUtil.isNotEmpty(activityInstance.getDeleteReason())){
                    activeActivityIds.add(activityId);
                }
            }else{
                isActive = true;
                viewerNodeInfo.setStatus(ProcessNodeStatusEnum.ACTIVE);
                activeActivityIds.add(activityId);
                //把激活的节点也存储到activeTask中
                addActiveTask(processViewerModel,activityInstance,variables,mainProcess);
            }
            viewerNodeInfo.setTaskId(activityInstance.getTaskId());
            viewerNodeInfo.setNodeName(activityInstance.getActivityName());
            viewerNodeInfo.setNodeId(activityId);
            viewerNodeInfo.setAssignee(activityInstance.getAssignee());
            viewerNodeInfo.setActivityType(activityInstance.getActivityType());
            //添加节点信息用于前端展示
            if("userTask".equals(activityInstance.getActivityType())){
                GraphicNodeInfo graphicNodeInfo = new GraphicNodeInfo();
                graphicNodeInfo.setStatus(ProcessNodeStatusEnum.COMPLETE);
                graphicNodeInfo.setName(activityInstance.getActivityName());
                graphicNodeInfo.setId(activityInstance.getActivityId());
                graphicNodeInfo.setTaskId(activityInstance.getTaskId());
                graphicNodeInfo.setAssignee(activityInstance.getAssignee());
                //活动名称必须为大写，同时必选按活动开始时间正序排序
                graphicNodeInfo.setActivityType("UserTask");
                processViewerModel.getNodes().add(0,graphicNodeInfo);
                //解析悬浮提示信息
                parseTip(viewerNodeInfo , commentMap , activityInstance);
                viewerNodeInfo.setNodeId(activityId);
            }
            // 处理多个任务时，节点状态显示不对的情况
            if(isActive){
                list.add(0,viewerNodeInfo);
            }else {
                list.add(viewerNodeInfo);
            }
            highlights.put(activityId, list);
            startedActivityIds.add(activityId);
        }

        //解析高亮线
        parseHighLightedLine(startedActivityIds,mainProcess,processViewerModel,activityInstances);

        // 删除多余的高亮信息
        deleteHighLighted(mainProcess,processViewerModel,activeActivityIds);
    }

    private void loadLaunch(ProcessViewerModel processViewerModel, HissProcessPreLaunch hissProcessPreLaunch){
        processViewerModel.setComments(new ArrayList<>());
        processViewerModel.setHighlights(new HashMap<>());
        processViewerModel.setActiveTasks(new ArrayList<>());
        processViewerModel.setNodes(new ArrayList<>());
        Model model = repositoryService.getModel(hissProcessPreLaunch.getModelId());
        List<String> deploymentResourceNames = repositoryService.getDeploymentResourceNames(model.getDeploymentId());
        for (String deploymentResourceName : deploymentResourceNames) {
            if(HissProcessConstants.PROCESS_RUN_XML.equals(deploymentResourceName)){
                InputStream inputStream = repositoryService.getResourceAsStream(model.getDeploymentId(), HissProcessConstants.PROCESS_RUN_XML);
                processViewerModel.setContent(IoUtil.readUtf8(inputStream));
            }
            if(HissProcessConstants.PROCESS_SHOW_CONFIG_JSON.equals(deploymentResourceName)){
                InputStream inputStream = repositoryService.getResourceAsStream(model.getDeploymentId(), HissProcessConstants.PROCESS_SHOW_CONFIG_JSON);
                processViewerModel.setConfigJson(IoUtil.readUtf8(inputStream));
            }
        }
        ProcessInfo processInfo = new ProcessInfo();
        processInfo.setStartUserId(hissProcessPreLaunch.getUserId());
        processInfo.setName(hissProcessPreLaunch.getModelName());
        processInfo.setStatus(ProcessStatusEnum.PREPARE);
        processInfo.setAdmin(false);
        processInfo.setTenantId(hissProcessPreLaunch.getTenantId());
        processInfo.setType(ModelTypeEnum.stringToEnum(model.getKey()));
        processInfo.setStartTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(hissProcessPreLaunch.getCreatedTime().getTime()), ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        processViewerModel.setProcessInfo(processInfo);
        // 加载表单
        setProcessForm(null,processViewerModel,hissProcessPreLaunch.getId() );
    }

    /**
     * 添加激活的节点信息
     *
     * @param processViewerModel
     * @param activityInstance
     * @param variables
     * @param mainProcess
     */
    private void addActiveTask(ProcessViewerModel processViewerModel, HistoricActivityInstance activityInstance, Map<String, Object> variables, Process mainProcess) {
        ActiveNodeInfo activeNodeInfo = new ActiveNodeInfo();
        activeNodeInfo.setTaskId(activityInstance.getTaskId());
        activeNodeInfo.setAssignee(activityInstance.getAssignee());
        activeNodeInfo.setActivityType(activityInstance.getActivityType());
        activeNodeInfo.setNodeType(parseMultiInstance(activityInstance,variables,mainProcess));// 解析激活节点的类型
        activeNodeInfo.setNodeId(activityInstance.getActivityId());
        parseTaskCandidate(activeNodeInfo);// 查询授权的用户
        processViewerModel.getActiveTasks().add(activeNodeInfo);
    }

    /**
     * 解析节点的类型
     * @param activityInstance
     * @param variables
     * @param mainProcess
     * @return
     */
    private ActiveNodeTypeEnum parseMultiInstance(HistoricActivityInstance activityInstance, Map<String, Object> variables, Process mainProcess) {
        //1.根据xml中的节点的处理类,来判断多实例的类型
        FlowNode flowNode = (FlowNode) mainProcess.getFlowElement(activityInstance.getActivityId());
        Object behavior = flowNode.getBehavior();
        if(behavior instanceof ParallelMultiInstanceBehavior){
            return ActiveNodeTypeEnum.MULTIPLE_PARALLEL;
        }
        if(behavior instanceof SequentialMultiInstanceBehavior){
            return ActiveNodeTypeEnum.MULTIPLE_SEQUENTIAL;
        }
        // 知会和抄送节点判断 从流程变量中获取
        if(variables.containsKey(HissProcessConstants.NODE_TYPE)){
            String typeName = (String) variables.get(HissProcessConstants.NODE_TYPE);
            if(StrUtil.isNotEmpty(typeName)){
                return ActiveNodeTypeEnum.valueOf(typeName);
            }
        }
        // 多人、开始、单人节点的识别
        if(behavior instanceof UserTaskActivityBehavior){
            return ActiveNodeTypeEnum.SINGLE_APPROVE;
        }
        // 返回默认值
        return ActiveNodeTypeEnum.NONE;
    }

    /**
     * 查询授权的用户
     * @param activeNodeInfo
     */
    private void parseTaskCandidate(ActiveNodeInfo activeNodeInfo) {
        if(StrUtil.isNotEmpty(activeNodeInfo.getTaskId())) {
            List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(activeNodeInfo.getTaskId());
            if(identityLinksForTask!=null) {
                Map<String, String> map = new HashMap<>();
                for (IdentityLink identityLink : identityLinksForTask) {
                    if(StrUtil.isNotEmpty(identityLink.getUserId())) {
                        map.put(identityLink.getUserId(), identityLink.getType());
                    }
                    if(StrUtil.isNotEmpty(identityLink.getGroupId())) {
                        map.put(identityLink.getGroupId(), identityLink.getType());
                    }
                }
                activeNodeInfo.setCandidateUser(map);
            }
        }
    }

    /**
     * 解析悬浮提示信息
     *
     * @param viewerNodeInfo
     * @param commentMap
     * @param activityInstance
     */
    private void parseTip(ViewerNodeInfo viewerNodeInfo, Map<String, ActHiComment> commentMap, HistoricActivityInstance activityInstance) {
        //- 对于有人审批的节点，则使用表格，显示每个节点操作人每次的操作明细
        //- 对于除审批之外的图例，比如开始、网关、中间事件、结束等图例，没有办理人，因此返回简单的时间信息即可
        viewerNodeInfo.setTipTitle(activityInstance.getActivityName());
        if("userTask".equals(activityInstance.getActivityType())){
            viewerNodeInfo.setTipTitle(activityInstance.getActivityName());
            // 人工：先评论信息
            String taskId = activityInstance.getTaskId();
            ActHiComment actHiComment = commentMap.get(taskId);
            ViewerTipInfo viewerTipInfo = new ViewerTipInfo();
            viewerTipInfo.setStartTime(simpleDateFormat.format(activityInstance.getTime()));
            long duration = 0;
            if (activityInstance.getEndTime()!=null){
                viewerTipInfo.setEndTime(simpleDateFormat.format(activityInstance.getEndTime()));
                duration = activityInstance.getEndTime().getTime()-activityInstance.getStartTime().getTime();
            }else{
                duration = System.currentTimeMillis()-activityInstance.getStartTime().getTime();
            }
            viewerTipInfo.setDuration(formatDuration(duration));
            // 如果有评论信息，才拼接信息
            if(actHiComment!=null) {
                viewerTipInfo.setUserId(actHiComment.getUserId());
                viewerTipInfo.setUserName(actHiComment.getUserId());
                viewerTipInfo.setOperateType(actHiComment.getMessage());
                viewerTipInfo.setOperateNote(actHiComment.getFullMsg());
            }else{
                viewerTipInfo.setUserId(activityInstance.getAssignee());
                viewerTipInfo.setUserName(activityInstance.getAssignee());
            }
            viewerTipInfo.setCacelReson(activityInstance.getDeleteReason());
            // 取消状态
            if(StrUtil.isNotEmpty(activityInstance.getDeleteReason())){
                viewerNodeInfo.setStatus(ProcessNodeStatusEnum.CANCEL);
            }
            viewerNodeInfo.setTipObject(viewerTipInfo);
        }else{
            viewerNodeInfo.setTipTitle(activityInstance.getActivityType());
            // 非人工：显示时间即可
            Date startTime = activityInstance.getStartTime();
            String format = simpleDateFormat.format(startTime);
            viewerNodeInfo.setTipContent(format);
        }
    }

    private void deleteHighLighted(Process mainProcess, ProcessViewerModel processViewerModel, Set<String> activeActivityIds) {
        Map<String, List<ViewerNodeInfo>> highlights = processViewerModel.getHighlights();
        for (String activeActivityId : activeActivityIds) {
            FlowNode flowNode = (FlowNode) mainProcess.getFlowElement(activeActivityId);
            deleteHighLightedNode(flowNode,highlights);
        }
    }

    public void deleteHighLightedNode(FlowNode flowNode,Map<String, List<ViewerNodeInfo>> highlights){
        if(flowNode!=null) {
            List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();
            if (outgoingFlows != null) {
                for (SequenceFlow outgoingFlow : outgoingFlows) {
                    highlights.remove(outgoingFlow.getId());
                    highlights.remove(outgoingFlow.getTargetRef());
                    FlowNode targetFlowNode = (FlowNode) outgoingFlow.getTargetFlowElement();
                    // 递归删除下一个节点
                    deleteHighLightedNode(targetFlowNode,highlights);
                }
            }
        }
    }

    /**
     * 解析高亮线
     *
     * @param startedActivityIds
     * @param processViewerModel
     * @param activityInstances
     */
    private void parseHighLightedLine(Set<String> startedActivityIds, Process mainProcess, ProcessViewerModel processViewerModel, List<HistoricActivityInstance> activityInstances) {

        Map<String, List<ViewerNodeInfo>> highlights = processViewerModel.getHighlights();
        for (HistoricActivityInstance activityInstance : activityInstances) {
            FlowNode flowNode = (FlowNode) mainProcess.getFlowElement(activityInstance.getActivityId());//查询xml中的活动信息
            if(flowNode!=null){
                List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();
                if(outgoingFlows!=null){
                    for (SequenceFlow outgoingFlow : outgoingFlows) {
                        String targetRef = outgoingFlow.getTargetRef();
                        //判断指向节点是否在活动实例表中
                        if(startedActivityIds.contains(targetRef)){
                            String lineId = outgoingFlow.getId();
                            ViewerNodeInfo viewerNodeInfo = new ViewerNodeInfo();
                            viewerNodeInfo.setStatus(ProcessNodeStatusEnum.COMPLETE);
                            viewerNodeInfo.setNodeId(lineId);
                            viewerNodeInfo.setActivityType("sequenceFlow");
                            highlights.put(lineId,Arrays.asList(viewerNodeInfo));
                        }
                    }
                }
            }
        }
    }

    /**
     * 查询xml源流程图
     * @param processInstanceId
     * @param processViewerModel
     * @return
     */
    private HistoricProcessInstance getHistoricProcessInstance(String processInstanceId, ProcessViewerModel processViewerModel) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(historicProcessInstance.getDeploymentId(), HissProcessConstants.PROCESS_RUN_XML);
        String xml = IoUtil.readUtf8(inputStream);
        processViewerModel.setContent(xml);
        return historicProcessInstance;
    }

    /**
     * 加载基本信息
     *
     * @param historicProcessInstance
     * @param processViewerModel
     * @param variables
     */
    private void processInfo(HistoricProcessInstance historicProcessInstance, ProcessViewerModel processViewerModel, Map<String, Object> variables) {
        ProcessInfo processInfo = new ProcessInfo();
        // 设置值 设置名称
        processInfo.setName(historicProcessInstance.getName());
        processInfo.setStartTime(simpleDateFormat.format(historicProcessInstance.getStartTime()));
        processInfo.setStartUserId(historicProcessInstance.getStartUserId());
        long duration = 0;// 耗时的毫秒数
        if(historicProcessInstance.getEndTime()!=null){
            processInfo.setEndTime(simpleDateFormat.format(historicProcessInstance.getEndTime()));
            duration = historicProcessInstance.getEndTime().getTime() - historicProcessInstance.getStartTime().getTime();
            processInfo.setStatus(ProcessStatusEnum.COMPLETE);
        }else{
            duration = System.currentTimeMillis() - historicProcessInstance.getStartTime().getTime();
            processInfo.setStatus(ProcessStatusEnum.ACTIVE);
        }
        processInfo.setEndReason(historicProcessInstance.getDeleteReason());
        processInfo.setDuration(formatDuration(duration));
        processInfo.setAdmin(false);
        processInfo.setType(ModelTypeEnum.DEV);
        processInfo.setTenantId(historicProcessInstance.getTenantId());
        processInfo.setParentProcessId("");
        // TODO 剩余PREPARE("预备中"),PENDING("挂起中"),CANCEL("已取消")，在后续进行补充
        //补充取消
        if (variables.containsKey(HissProcessConstants.PROCESS_STATUS)){
            String status = (String) variables.get(HissProcessConstants.PROCESS_STATUS);
            if(ProcessStatusEnum.CANCEL.name().equals(status)){
                processInfo.setStatus(ProcessStatusEnum.CANCEL);
            }
            if(ProcessStatusEnum.REJECT.name().equals(status)){
                processInfo.setStatus(ProcessStatusEnum.REJECT);
            }
        }
        processViewerModel.setProcessInfo(processInfo);
    }

    // TimeUnit可以把毫秒转成其它的数值单位时间
    public static String formatDuration(long durationInMillis) {
        long days = TimeUnit.MILLISECONDS.toDays(durationInMillis);
        long hours = TimeUnit.MILLISECONDS.toHours(durationInMillis) % 24;
        long minutes = TimeUnit.MILLISECONDS.toMinutes(durationInMillis) % 60;
        long seconds = TimeUnit.MILLISECONDS.toSeconds(durationInMillis) % 60;
        long milliseconds = durationInMillis % 1000;

        StringBuilder result = new StringBuilder();
        if (days > 0) {
            result.append(days).append("天 ");
        }
        if (hours > 0) {
            result.append(hours).append("小时 ");
        }
        if (minutes > 0) {
            result.append(minutes).append("分钟 ");
        }
        if (seconds > 0) {
            result.append(seconds).append("秒 ");
        }
        if (milliseconds > 0) {
            result.append(milliseconds).append("毫秒 ");
        }

        return result.toString().trim();
    }

    @Override
    public String getId() {
        return HandlerIdClientEnum.PI_GET_INSTANCE_DEV_VIEWER.getId();
    }
}